﻿/*
 * 
 * 
 * 
 *      软件名称：  克微民康 基础类库                           
 *          说明：  克微民康复用公共数据类型及数据库接口平台。
 *          架构：  多层、C/S                                （C/S、B/S、多层、分布、移动）。
 *      最终版本：  Ver 1.1
 *          语言：  C# 
 *      名称空间：　KWMK.Drawing.Printing
 * 
 *      文件属性：  基础共享库                          （基础共享库，项目共享库，多层架构，业务代码，用户界面，配置系统、资源。）
 * 
 *       XML文档：  是                                  （是、否。）
 * 
 *          作者：  杨士信（yangsx）     
 * 
 *      创建日期：  2006-8-4  至  2008-9-20
 *      创建内容：  建立一个跨越项目、软件的通用服务对象平台。
 *      创建版本：  Ver 0.01
 * 
 *        修改人：                                      （每次修改请在这里追加，并修改最终版本号。）
 *      修改日期：
 *      修改内容：
 *      修改版本：   
 * 
 * 
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections;
namespace YDSOO.Drawing.Printing
{
    public sealed class YDFormPrint
    {
        #region 颜色深度定义

        /// <summary>
        /// 颜色深度定义
        /// </summary>
        public enum ColorDepthReduction
        {
            /// <summary>
            /// 无
            /// </summary>
            None,
            /// <summary>
            /// 8位彩色
            /// </summary>
            Colored8bpp,
            /// <summary>
            /// 灰色
            /// </summary>
            Grayscaled8bpp
        }

        #endregion

        #region 成员、构造、事件、附加方法
        /// <summary>
        /// 内部打印对象实例
        /// </summary>
        private static YDFormPrint cls;
        /// <summary>
        /// 打印控件可以是窗体
        /// </summary>
        private Control ctrl;
        private Bitmap bmp;
        /// <summary>
        /// 打印开关
        /// </summary>
        private bool Printing;
        /// <summary>
        /// 格式化及转换
        /// </summary>
        private ImageFormat formatImage;
        private ColorDepthReduction format8bitPixel;

        //打印参数
        private string PrinterName;
        private Margins PrinterMargins;
        private bool PrinterPortrait;

        //  附加存盘参数
        private string SaveFileName;
        /// <summary>
        /// 私有构造，禁止实例化
        /// </summary>
        private YDFormPrint()
        {
        }
        /// <summary>
        /// 私有构造，禁止实例化，初始化所有参数
        /// </summary>
        /// <param name="Ctrl">控件或窗体</param>
        /// <param name="bPrint">是否打印</param>
        /// <param name="fImage">颜色数</param>
        /// <param name="f8bitPixel">色深</param>
        /// <param name="sPrinterName">打印机名称</param>
        /// <param name="aMargins">边距</param>
        /// <param name="bPortrait">横打</param>
        /// <param name="sFileName">保存文件名称</param>
        private YDFormPrint(Control ctrl, bool bPrint, ImageFormat fImage, ColorDepthReduction f8bitPixel, string sPrinterName,
            Margins aMargins,bool bPortrait,string sSaveFile)
        {
            Trace.Assert(ctrl != null);
            this.ctrl = ctrl;

            if (Find_RTF_or_Web(ctrl))
                bmp = GetBitmapGDI(ctrl);
            else
                bmp = GetBitmap(ctrl);
            Printing = bPrint;
            formatImage = fImage;
            format8bitPixel = f8bitPixel;
            PrinterName = sPrinterName;
            PrinterMargins = aMargins;
            PrinterPortrait = bPortrait;
            SaveFileName = sSaveFile;
        }

        /// <summary>
        /// 是否是RTF控件或者浏览器
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <returns>是否</returns>
        private bool Find_RTF_or_Web(Control ctrl)
        {
            if (ctrl.Controls.Count == 0)
                return (ctrl is RichTextBox) || (ctrl is WebBrowserBase);
            else
            {
                bool result = false;
                foreach (Control  c in ctrl.Controls)
                {
                    result = Find_RTF_or_Web(c);
                    if (result)
                        break;                    
                }
                return result;
            }
        }

        /// <summary>
        /// 使用打印机绘图设备在位图上绘制
        /// </summary>
        /// <param name="sender">对象</param>
        /// <param name="e">事件</param>
        private void doc_PrintPage(System.Object sender,
                                   System.Drawing.Printing.PrintPageEventArgs e)
        {
            e.Graphics.DrawImage(bmp, 0, 0);
        }
        /// <summary>
        /// 得到缩略位图
        /// </summary>
        /// <param name="newWidth">位图要求的最小宽度</param>
        /// <param name="newHeight">位图要求的最小高度</param>
        private void GetThumbnail(int newWidth, int newHeight)
        {
            Bitmap result = new Bitmap(newWidth, newHeight);
            Graphics g = Graphics.FromImage(result);
            g.FillRectangle(new SolidBrush(Color.White), 0, 0, result.Width, result.Height);

            float factor = Math.Max((float)bmp.Width / (float)newWidth, (float)bmp.Height / (float)newHeight);
            g.DrawImage(bmp, 0, 0, (float)bmp.Width / factor, (float)bmp.Height);
            g.Dispose();

            bmp.Dispose();
            bmp = result;
        }
        #endregion

        #region 复制控件位图
        private Bitmap GetBitmap(Control control)
        {
            Graphics grpCtrl = control.CreateGraphics();
            Bitmap Result = new Bitmap(control.Width, control.Height,grpCtrl);
            control.DrawToBitmap(Result,new Rectangle(0,0,control.Width,control.Height));
            return Result;
        }

        [DllImport("gdi32.dll")]
        private static extern int BitBlt(IntPtr hdcDest, int xDest, int yDest, int nWidth, int nHeight, IntPtr hdcSource, int xSrc, int ySrc, Int32 dwRop);

        const int SRCCOPY = 13369376;

        private Bitmap GetBitmapGDI(Control control)
        {
            Graphics grpCtrl = Graphics.FromHwnd(control.Handle);
            Bitmap Result = new Bitmap(control.Width, control.Height, grpCtrl);
            IntPtr hdcCtrl = grpCtrl.GetHdc();

            Graphics grpDest = Graphics.FromImage(Result);
            IntPtr hdcDest = grpDest.GetHdc();

            int sourceX, sourceY;
            if (control is Form)
            {
                sourceX = control.ClientSize.Width - control.Width + 4;
                sourceY = control.ClientSize.Height- control.Height + 4;
            }
            else
            {
                sourceX = 0;
                sourceY = 0;
            }
            BitBlt(hdcDest, 0, 0, control.Width, control.Height, hdcCtrl, sourceX, sourceY, SRCCOPY);
            grpCtrl.ReleaseHdc(hdcCtrl);
            grpDest.ReleaseHdc(hdcDest);
            return Result;
        }        
        #endregion

        #region 转换位图颜色深度

        Color ConvertColorTo8Bpp(Color color)
        {
            int r = color.R - color.R % 51;
            int g = color.G - color.G % 51;
            int b = color.B - color.B % 51;

            return Color.FromArgb(r, b, g);
        }

        Color ConvertColorTo8Bpp(Color color, int correction)
        {
            int r = (color.R + correction) - (color.R + correction) % 51;
            int g = (color.G + correction) - (color.G + correction) % 51;
            int b = (color.B + correction) - (color.B + correction) % 51;

            return Color.FromArgb(r, b, g);
        }
        private void ConvertBitmapTo8Bpp()
        {
            ConvertBitmapTo8Bpp(0);
        }

        public void ConvertBitmapTo8Bpp(int correction)
        { 
            using(ProgressForm progress = new ProgressForm(ctrl))
            {
                if (bmp.Width * bmp.Height > 4000)
                    progress.Show();
                DateTime now;
                TimeSpan diff;
                Bitmap result = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format8bppIndexed);
                BitmapData resultData = result.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
                int tmpStride = resultData.Stride;

                IntPtr ptr = resultData.Scan0;
                Hashtable ColorReduction = new Hashtable();
                byte offset = 39;
                for (int r = 0; r < 256; r += 51)
                {
                    for (int g = 0; g < 256; g += 51)
                        for (int b = 0; b < 256; b += 51)
                        {
                            ColorReduction[Color.FromArgb(r, b, g)] = ++offset;
                        }
                }
                int tmpBytes = bmp.Height * tmpStride;
                byte[] tmpBitmapData = new byte[tmpBytes];
                now = DateTime.Now;
                for (int x = 0; x < bmp.Width; x++)
                {
                    diff = DateTime.Now - now;
                    if (progress.Visible && (diff.Ticks > 6000000))
                    {
                        progress.Progressing(x);
                        now = DateTime.Now;
                    }
                    for (int y = 0; y < bmp.Height; y++)
                    {
                        int offset2 = (y * tmpStride) + x;
                        Color col = ConvertColorTo8Bpp(bmp.GetPixel(x, y), correction);
                        if (format8bitPixel == ColorDepthReduction.Colored8bpp)
                            tmpBitmapData[offset2]
                                = (byte)(col.R * 0.3 + col.G * 0.59 + col.B * 0.11);
                        else
                            tmpBitmapData[offset2]
                                = (byte)ColorReduction[col];
                    }
                }
                System.Runtime.InteropServices.Marshal.Copy(tmpBitmapData, 0, ptr, tmpBytes);
                result.UnlockBits(resultData);
                progress.Hide();
                bmp.Dispose();
                bmp = result;
            }
        }
        #endregion

        #region 附加的进度窗口
        private class ProgressForm : System.Windows.Forms.Form
        {
            private System.ComponentModel.Container components = null;
            private System.Windows.Forms.Label showValue;
            private int maxValue = 100;

            public ProgressForm()
            { }

            /// <summary>
            /// 采用控件实例化
            /// </summary>
            /// <param name="Ctrl">控件</param>
            public ProgressForm(Control control)
            {
                InitializeComponent(control);
                maxValue = control.Width;
            }
            /// <summary>
            /// 清除资源
            /// </summary>
            /// <param name="disposing">是否托管</param>
            protected override void Dispose(bool disposing)
            {
                if (disposing)
                {
                    if (components != null)
                    {
                        components.Dispose();
                    }
                }
                base.Dispose(disposing);
            }
            private void InitializeComponent(Control control)
            {
                this.showValue = new System.Windows.Forms.Label();
                this.SuspendLayout();
                //
                // showValue
                //
                this.showValue.Dock = System.Windows.Forms.DockStyle.Fill;
                this.showValue.Location = new System.Drawing.Point(0, 0);
                this.showValue.Name = "showValue";
                this.showValue.Size = new System.Drawing.Size(140, 22);
                this.showValue.TabIndex = 0;
                this.showValue.Text = "0 %";
                this.showValue.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
                //
                // ProgressForm
                //
                this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
                this.ClientSize = new System.Drawing.Size(140, 22);
                this.ControlBox = false;
                this.Controls.Add(this.showValue);
                this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;

                //
                //  check manual position
                Point pt = control.PointToScreen(new Point(0, 0));
                this.Location = new System.Drawing.Point(
                    pt.X + (control.Width - this.Width) / 2,
                    pt.Y + (control.Height - this.Height) / 3);
                this.MaximizeBox = false;
                this.MinimizeBox = false;
                this.Name = "ProgressForm";
                this.ShowInTaskbar = false;
                this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
                this.TopMost = true;
                this.ResumeLayout(false);
            }

            /// <summary>
            /// 更新进度
            /// </summary>
            /// <param name="Position">新位置</param>
            public void Progressing(int Position)
            {
                showValue.Text = String.Format("转换图像 {0} %", (int)((Position * 100) / maxValue));
                showValue.Refresh();
            }
        }

        #endregion

        #region 预览、打印、保存

        /// <summary>
        /// Start混合了打印和保存，静态
        /// </summary>
        /// <param name="Ctrl">控件</param>
        /// <param name="bPrint">是否打印</param>
        /// <param name="fImage">打印格式</param>
        /// <param name="f8bitPixel">颜色深度</param>
        /// <param name="sPrinterName">打印机名称</param>
        /// <param name="aMargins">边距</param>
        /// <param name="bPortrait">纵向打印</param>
        /// <param name="sFileName">保存文件名称</param>
        private static void Start(Control Ctrl, bool bPrint, ImageFormat fImage, ColorDepthReduction f8bitPixel, string sPrinterName, Margins aMargins, bool bPortrait, string sFileName)
        {
            Trace.Assert(Ctrl != null);

            if (fImage == null)
                fImage = ImageFormat.Png;
            if (bPrint)
            {
                if (sPrinterName == null)
                    sPrinterName = "";
            }
            else
            {
                if (String.IsNullOrEmpty(sFileName))
                    sFileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), Ctrl.Name + "." + fImage.ToString());
            }
            cls = new YDFormPrint(Ctrl, bPrint, fImage, f8bitPixel, sPrinterName, aMargins, bPortrait, sFileName);
            try
            {
                if (bPrint)
                    cls.StartPrinting();
                else
                    cls.StartSaving();
            }
            finally
            {
                cls.bmp.Dispose();
                cls.bmp = null;
                Ctrl.Refresh();
            }
        }

        /// <summary>
        /// 打印控件位图
        /// </summary>
        private void StartPrinting()
        {
            using (PrintDocument doc = new PrintDocument())
            {
                try
                {
                    if (!String.IsNullOrEmpty(PrinterName))
                        doc.PrinterSettings.PrinterName = this.PrinterName;
                    bool bPrinterValid = doc.PrinterSettings.IsValid;
                    doc.DefaultPageSettings.Landscape = !PrinterPortrait;
                    if (PrinterMargins != null)
                        doc.DefaultPageSettings.Margins = PrinterMargins;
                    doc.PrintPage += new PrintPageEventHandler(doc_PrintPage);
                    if (PrinterMargins != null)
                    {
                        doc.OriginAtMargins = true;
                        PageSettings stg = doc.DefaultPageSettings;
                        float maxX = (stg.PaperSize.Width - stg.Margins.Left - stg.Margins.Right);
                        float maxY = (stg.PaperSize.Height - stg.Margins.Top - stg.Margins.Bottom);
                        float bmpX = maxX * 96 / 100;
                        float bmpY = maxY * 96 / 100;
                        if (stg.Landscape)
                        {
                            {
                                float tmp = bmpX;
                                bmpX = bmpY;
                                bmpY = tmp;
                            }
                        }
                        if ((bmp.Width > bmpX) || (bmp.Height > bmpY))
                        {
                            GetThumbnail((int)bmpX, (int)bmpY);
                        }
                    }
                    if (format8bitPixel != ColorDepthReduction.None)
                        ConvertBitmapTo8Bpp(15);
                    doc.DocumentName = ctrl.Name;
                    doc.Print();
                }
                catch (InvalidPrinterException e)
                {
                    MessageBox.Show(e.Message, "YDFormPrint.StartPrinting - 打印错误");
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message, "YDFormPrint.StartPrinting - 普通错误");
                    throw;
                }
                finally
                {
                    doc.PrintPage -= new PrintPageEventHandler(doc_PrintPage);
                }
            }
        }

        /// <summary>
        /// 保存控件位图
        /// </summary>
        private void StartSaving()
        {
            try
            {
                bmp.Save(SaveFileName, formatImage);
            }
            catch (System.IO.IOException e)
            {
                MessageBox.Show("位图未能保存:\n" +
                                SaveFileName + "\n" +
                                e.Message,
                                "FormPrint.StartSaving - 文件错误");
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, "KWFormPrint.StartSaving - 普通错误");
                throw;
            }
        }

        #endregion

        #region 打印方法

        /// <summary>
        /// 打印，指定参数，静态
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="f8bitPixel">色深</param>
        /// <param name="sPrinterName">打印机名称 </param>
        /// <param name="aMargins">边距</param>
        /// <param name="bPortrait">纵向</param>
        public static void Print(Control ctrl, ColorDepthReduction f8bitPixel,
                string sPrinterName, Margins aMargins, bool bPortrait)
        {
            Start(ctrl, true, null, f8bitPixel,
                  sPrinterName, aMargins, bPortrait, null);
            return;
        }

        /// <summary>
        /// 打印，PNG格式，指定参数，静态
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="sPrinterName">打印机名称</param>
        /// <param name="aMargins">边距</param>
        /// <param name="bPortrait">纵向</param>
        public static void Print(Control ctrl,
                string sPrinterName, Margins aMargins, bool bPortrait)
        {
            Start(ctrl, true, null, ColorDepthReduction.None,
                  sPrinterName, aMargins, bPortrait, null);
            return;
        }

        /// <summary>
        /// 打印，8色深格式，指定参数，静态
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="f8bitPixel">色深</param>
        /// <param name="sPrinterName">打印机名称</param>
        /// <param name="bPortrait">纵向</param>
        public static void Print(Control ctrl, ColorDepthReduction f8bitPixel,
                string sPrinterName, bool bPortrait)
        {
            Start(ctrl, true, null, f8bitPixel,
                  sPrinterName, null, bPortrait, null);
            return;
        }

        /// <summary>
        ///  打印，PNG格式，指定参数，静态
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="sPrinterName">打印机名称</param>
        /// <param name="bPortrait">纵向</param>
        public static void Print(Control ctrl, string sPrinterName, bool bPortrait)
        {
            Start(ctrl, true, null, ColorDepthReduction.None,
                  sPrinterName, null, bPortrait, null);
            return;
        }

        /// <summary>
        /// 打印，标准打印机，指定参数，静态
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="f8bitPixel">色深</param>
        /// <param name="aMargins">边距</param>
        /// <param name="bPortrait">纵向</param>
        public static void Print(Control ctrl, ColorDepthReduction f8bitPixel,
                Margins aMargins, bool bPortrait)
        {
            Start(ctrl, true, null, f8bitPixel, null, aMargins, bPortrait, null);
            return;
        }

        /// <summary>
        /// 打印，灰色深格式，指定参数，静态
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="f8bitPixel">色深</param>
        /// <param name="bPortrait">纵向</param>
        public static void Print(Control ctrl, ColorDepthReduction f8bitPixel,
                bool bPortrait)
        {
            Start(ctrl, true, null, f8bitPixel, null, null, bPortrait, null);
            return;
        }

        /// <summary>
        /// 打印，灰色深格式，指定参数，静态
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="f8bitPixel">色深</param>
        /// <param name="aMargins">边距</param>
        public static void Print(Control ctrl, ColorDepthReduction f8bitPixel,
                Margins aMargins)
        {
            Start(ctrl, true, null, f8bitPixel, null, aMargins, true, null);
            return;
        }

        /// <summary>
        /// 打印，灰色深格式，指定参数，静态
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="aMargins">边距</param>
        /// <param name="bPortrait">纵向</param>
        public static void Print(Control ctrl, Margins aMargins, bool bPortrait)
        {
            Start(ctrl, true, null, ColorDepthReduction.None,
                  null, aMargins, bPortrait, null);
            return;
        }

        /// <summary>
        /// 打印，指定参数，静态
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="aMargins">边距</param>
        public static void Print(Control ctrl, Margins aMargins)
        {
            Start(ctrl, true, null, ColorDepthReduction.None, null, aMargins, true, null);
            return;
        }

        /// <summary>
        /// 打印，指定参数，静态
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="bPortrait">纵向</param>
        public static void Print(Control ctrl, bool bPortrait)
        {
            Start(ctrl, true, null, ColorDepthReduction.None, null, null, bPortrait, null);
            return;
        }

        /// <summary>
        /// 打印，指定参数，静态
        /// </summary>
        /// <param name="ctrl">控件</param>
        public static void Print(Control ctrl)
        {
            Start(ctrl, true, null, ColorDepthReduction.None, null, null, true, null);
            return;
        }

        #endregion

        #region 保存到文件

        /// <summary>
        /// 保存到文件
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="fImage">位图格式</param>
        /// <param name="sFileName">文件名称</param>
        public static void Save(Control ctrl, ImageFormat fImage, string sFileName)
        {
            Start(ctrl, false, fImage, ColorDepthReduction.None,
                  null, null, false, sFileName);
            return;
        }

        /// <summary>
        /// 保存到文件
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="fImage">位图格式</param>
        public static void Save(Control ctrl, ImageFormat fImage)
        {
            Start(ctrl, false, fImage, ColorDepthReduction.None, null, null, false, null);
            return;
        }

        /// <summary>
        /// 保存到文件
        /// </summary>
        /// <param name="ctrl">控件</param>
        /// <param name="sFileName">文件名称</param>
        public static void Save(Control ctrl, string sFileName)
        {
            Start(ctrl, false, null, ColorDepthReduction.None, null, null, false, sFileName);
            return;
        }

        /// <summary>
        /// 保存到文件
        /// </summary>
        /// <param name="ctrl">控件</param>
        public static void Save(Control ctrl)
        {
            Start(ctrl, false, null, ColorDepthReduction.None, null, null, false, null);
            return;
        }

        #endregion 
    }
}
